// Programme de test pour le format tdf

#include <cstdio>
#include <time.h>
#include <windows.h>
#include <cstring>
#include <vector>

// Libs du format tdf:

#include "label.h"
#include "variant.h"
#include "table.h"
#include "node.h"
#include "tdf.h"

//-------------------------------------

#define numtry 1000000

using namespace std;

void printType( variant* var ){
    printf("VarType: %s\n",variant::typeToString( var->getType() ));
}

char getChar(unsigned char* ptr){
    #define WILDCARD '.'
    char stack = *((char*) ptr);
    if( stack < 0x20 || stack > 0x7e ) stack = WILDCARD;
    return stack;
}
#define Hex(s) (*((unsigned char*)s))
void stackPrint(unsigned char* start,unsigned int size){

    // Format de type: xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | cccc cccc cccc cccc
    #define FORMAT              "%4d: %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x | %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c\n"
    #define FORMAT_BEGLN        "%4d: "
    #define FORMAT_BEGIN        "%02x"
    #define FORMAT_BEGIN_EMPTY  "  "
    #define FORMAT_BEGIN_SPARE  " "
    #define FORMAT_SEPARATOR    " | "
    #define FORMAT_END          "%c"
    #define FORMAT_END_EMPTY    " "
    #define FORMAT_END_SPARE    " "
    #define FORMAT_ENDLN        "\n"

    unsigned int lines = size / 16;
    unsigned char uncomplete = 0;
    if( size % 16 ) uncomplete = 16 - size%16;

    for( unsigned int i = 0; i < lines; i++ ){
        printf(FORMAT,i*16,
            Hex(start+i*16),Hex(start+i*16+1),Hex(start+i*16+2),Hex(start+i*16+3),
            Hex(start+i*16+4),Hex(start+i*16+5),Hex(start+i*16+6),Hex(start+i*16+7),
            Hex(start+i*16+8),Hex(start+i*16+9),Hex(start+i*16+10),Hex(start+i*16+11),
            Hex(start+i*16+12),Hex(start+i*16+13),Hex(start+i*16+14),Hex(start+i*16+15),
            getChar( start+i*16 ),getChar( start+i*16+1 ),getChar( start+i*16+2 ),getChar( start+i*16+3 ),
            getChar( start+i*16+4 ),getChar( start+i*16+5 ),getChar( start+i*16+6 ),getChar( start+i*16+7 ),
            getChar( start+i*16+8 ),getChar( start+i*16+9 ),getChar( start+i*16+10 ),getChar( start+i*16+11 ),
            getChar( start+i*16+12 ),getChar( start+i*16+13 ),getChar( start+i*16+14 ),getChar( start+i*16+15 )
        );
    }
    if( uncomplete ){
        printf(FORMAT_BEGLN,lines*16);
        for( int i = 0; i < 16; i++ ){
            if( i != 0 && !(i%4) ) printf(FORMAT_BEGIN_SPARE);
            if( i < uncomplete ) printf(FORMAT_BEGIN,Hex(start+size-uncomplete+i));
            else printf(FORMAT_BEGIN_EMPTY);
        }
        printf(FORMAT_SEPARATOR);
        for( int i = 0; i < 16; i++ ){
            if( i != 0 && !(i%4) ) printf(FORMAT_END_SPARE);
            if( i < uncomplete ) printf(FORMAT_END,getChar(start+size-uncomplete+i));
            else printf(FORMAT_END_EMPTY);
        }
        printf(FORMAT_ENDLN);
    }

}

void getArgs(vector<string>* lst){
    char buffer[1024];
    do{
        scanf("%s",buffer);
        lst->push_back(string(buffer));
        if( cin.peek() == '\n' ) return;
    }while(1);
}

void display_help(){
    printf("TDF utility 1.0 - Aide\n");
    printf("----------------------\n");
    printf("add node [nodename]\n\t- Ajoute le noeud au noeud actuel\n\n");
    printf("clear\n\t- Reinitialise le descripteur\n\n");
    printf("current\n\t- Affiche la position actuelle dans l'arborescence\n\n");
    printf("export [filename]\n\t- Exporte le contenu du descripteur dans le fichier\n\n");
    printf("export-xml [filename]\n\t- Exporte le contenu du descripteur dans le fichier au format XML\n\n");
    printf("goto \n  ..\t\t\t- Accede au noeud parent\n");
    printf("  [nodename]\t\t- Accede au premier noeud nomme [nodename]\n");
    printf("  [nodename] [num]\t- Accede au [num]ieme noeud nomme [nodename]\n\n");
    printf("import [filename]\n\t- Importe le descripteur a partir du fichier TDF\n\n");
    printf("list\n\t- lister les donnees du noeud actuel\n");
    printf("  all\t- lister l'ensemble du contenu du descripteur de format\n");
    printf("  sub\t- lister les donnees du noeud actuel et de l'arborescence en decoulant\n");
    printf("  nt\t- liter les tables des matieres\n\n");
    printf("quit\n\t- quitter l'application\n\n");
    printf("setattr [attributename] [attributetype] [value]\n\t- Ajoute ou modifie l'attribut\n\n");
    printf("setval [valtype] [value]\n\t-Fixe la valeur du noeud\n\n");
    printf("\tPour les deux fonctions precedentes, les valeurs \"type\"\n\tpeuvent prendre les etats suivants:\n");
    printf("\tuint, int, ush, sh, ulong, long, byte, char, flt, dbl, str, undef\n");
    printf("\tLes types undef et string ne necessitent pas de valeur \n\t(pour string, on vous la demande par la suite)\n");
    printf("\n");
}

void list(tdf_node* node,bool list_sub = true,string cat = ""){
    printf("%sN %s\n",cat.c_str(),node->getName().c_str());
    if( node->value.getType() != VAR_UNDEF ){
        printf("%s  V %s:%s\n",cat.c_str(),variant::typeToShortString(node->value.getType()),node->value.toString().c_str());
    }
    if( node->countAttrib() ){
        tdf_attribute* stattr = node->getFirstAttrib();
        do{
            printf("%s  A %s | %s:%s\n",cat.c_str(),stattr->getName().c_str(),variant::typeToShortString(stattr->value.getType()),stattr->value.toString().c_str());
        }while( (stattr = node->getNextAttrib(stattr)) );
    }
    if( node->countChildNode() ){
        tdf_node* stnode = node->getFirstChildNode();
        do{
            if( list_sub ) list(stnode,true,cat+"  ");
            else printf("%s  N %s\n",cat.c_str(),stnode->getName().c_str());
        }while( (stnode = node->getNextChildNode( stnode )) );
    }
}

int main( int args, char** argv ){

    tdf td;
    tdf_node* node_stack = &td;
    tdf_attribute* attrib_stack;

    printf("TDF utility v 1.0\n----------------------------------\nTapez \"help\" pour connaitre les commandes disponibles\n\n");

    vector<string> commands;
    bool quit = false;

    do{
        getArgs(&commands);

        #if false
            for( vector<string>::iterator i = commands.begin(); i < commands.end(); i++ ){
                printf("%s ",(*i).c_str());
            }
            printf("\n");
        #endif

        if( commands.at(0) == "quit" ){
            quit = true;
        }
        else if( commands.at(0) == "help" ){
            display_help();
        }
        else if( commands.at(0) == "list" ){
            if( commands.size() < 2 ){
                list(node_stack,false);
                printf("\n");
            }
            else if( commands.at(1) == "all" ){
                list(&td);
                printf("\n");
            }
            else if( commands.at(1) == "sub" ){
                list(node_stack);
                printf("\n");
            }
            else if( commands.at(1) == "nt" ){
                printf("Nodes NT:\n");
                for( unsigned int i = 0; i < td.nodesNT.getNumName(); i++ ){
                    printf("  %s\n", td.nodesNT.getName(i).c_str());
                }
                printf("\nAttributes NT:\n");
                for( unsigned int i = 0; i < td.attributesNT.getNumName(); i++ ){
                    printf("  %s\n", td.attributesNT.getName(i).c_str());
                }
                printf("\n");
            }
            else{
                printf("Argument 1:%s non reconnu\n\n", commands.at(1).c_str());
            }
        }
        else if( commands.at(0) == "clear" ){
            td.clear();
            node_stack = &td;
            printf("Descripteur reinitialise\n\n");
        }
        else if( commands.at(0) == "add" ){
            if( commands.size() < 3 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else if( commands.at(1) == "node" ){
                printf("Ajout du noeud %s\n\n",commands.at(2).c_str());
                node_stack->addChildNode( &commands.at(2) );
            }
            else{
                printf("Argument 1:%s non reconnu\n\n", commands.at(1).c_str());
            }
        }
        else if( commands.at(0) == "goto"){
            if( commands.size() < 2 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else if( commands.at(1) == ".." ){
                tdf_node* stk;
                if( (stk = node_stack->getParentNode()) ){
                    node_stack = stk;
                    printf("\n");
                }
                else{
                    printf("Le noeud actuel est deja a la racine\n\n");
                }
            }
            else if( commands.size() > 2 ){
                tdf_node* stk;
                if( (stk = node_stack->getChildNodeAsAt( &commands.at(1), atoi( commands.at(2).c_str() ) )) ){
                    node_stack = stk;
                    printf("\n");
                }
                else{
                    printf("Noeud %s[%d] introuvable\n\n",commands.at(1).c_str(),atoi( commands.at(2).c_str() ));
                }
            }
            else {
                tdf_node* stk;
                if( (stk = node_stack->getFirstChildNodeAs( &commands.at(1) )) ){
                    node_stack = stk;
                    printf("\n");
                }
                else{
                    printf("Noeud %s[0] introuvable\n\n",commands.at(1).c_str());
                }
            }
        }
        else if( commands.at(0) == "current"){
            printf("Position actuelle:\n%s\n\n",node_stack->getPath().c_str());
        }
        else if( commands.at(0) == "export" ){
            if( commands.size() < 2 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else{
                td.exportTDF( (char*)commands.at(1).c_str() );
                printf("Termine.\n\n");
            }
        }
        else if( commands.at(0) == "export-xml" ){
            if( commands.size() < 2 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else{
                td.exportXML( (char*)commands.at(1).c_str() );
                printf("Termine.\n\n");
            }
        }
        else if( commands.at(0) == "import" ){
            if( commands.size() < 2 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else{
                td.importTDF( (char*)commands.at(1).c_str() );
                node_stack = &td;
                printf("Termine.\n\n");
            }
        }
        else if( commands.at(0) == "setattr" ){
            if( commands.size() < 3 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else{
                variant var;
                var.fromStrings( (char*)commands.at(2).c_str() , (char*)((commands.size() > 3)? commands.at(3).c_str() : NULL) );
                if( var.getType() != VAR_UNDEF ){
                    if( !(attrib_stack = node_stack->getAttrib( &commands.at(1) ) ) )
                        attrib_stack = node_stack->addAttrib( &commands.at(1) );
                    attrib_stack->value.copy(&var);
                }
                printf("\n");
            }
        }
        else if( commands.at(0) == "setval" ){
            if( commands.size() < 2 ){
                printf("Nombre d'arguments insuffisant\n\n");
            }
            else{
                node_stack->value.fromStrings( (char*)commands.at(1).c_str(), (char*)((commands.size() > 2)? commands.at(2).c_str():NULL));
                printf("\n");
            }
        }
        else{
            printf("Commande %s non reconnue\n\n",commands.at(0).c_str());
        }

        commands.clear();
    }while(!quit);

    exit(EXIT_SUCCESS);
}
